package ru.lukyanets.transmission.client.ui.control.skin;

import javafx.css.CssMetaData;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.css.converter.PaintConverter;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import ru.lukyanets.transmission.client.ui.control.PiecesIndicator;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author Sergey Lukyanets
 */
public class PiecesIndicatorSkin extends SkinBase<PiecesIndicator> {

    private final PiecesIndicator control;

    private BitsIndicator indicator;

    /**
     * Constructor for all SkinBase instances.
     *
     * @param control The control for which this Skin should attach to.
     */
    public PiecesIndicatorSkin(PiecesIndicator control) {
        super(control);

        this.control = control;

        registerChangeListener(control.countProperty, e -> update());
        registerChangeListener(control.bitsProperty(), e -> update());

        initialize();
    }

    private void initialize() {
        indicator = new BitsIndicator(control, this, segmentColor.get());
        indicator.getStyleClass().setAll("indicator");

        getChildren().setAll(indicator);
    }

    /**
     * The colour of the progress segment.
     */
    private final StyleableObjectProperty<Paint> segmentColor = new StyleableObjectProperty<>(null) {
        @Override
        protected void invalidated() {
            final Paint value = get();
            if (value != null && !(value instanceof Color)) {
                if (isBound()) {
                    unbind();
                }
                set(null);
                throw new IllegalArgumentException("Only Color objects are supported");
            }
            if (indicator != null) indicator.setFillOverride(value);
        }

        @Override
        public Object getBean() {
            return PiecesIndicatorSkin.this;
        }

        @Override
        public String getName() {
            return "segmentColorProperty";
        }

        @Override
        public CssMetaData<PiecesIndicator, Paint> getCssMetaData() {
            return SEGMENT_COLOR;
        }
    };

    @SuppressWarnings("unused")
    Paint getSegmentColor() {
        return segmentColor.get();
    }


    private static final CssMetaData<PiecesIndicator, Paint> SEGMENT_COLOR =
            new CssMetaData<>("-fx-segment-color",
                    PaintConverter.getInstance(), null) {

                @Override
                public boolean isSettable(PiecesIndicator n) {
                    final PiecesIndicatorSkin skin = (PiecesIndicatorSkin) n.getSkin();
                    return !skin.segmentColor.isBound();
                }

                @Override
                public StyleableProperty<Paint> getStyleableProperty(PiecesIndicator n) {
                    final PiecesIndicatorSkin skin = (PiecesIndicatorSkin) n.getSkin();
                    return skin.segmentColor;
                }
            };


    /**
     * {@inheritDoc}
     */
    @Override
    protected void layoutChildren(final double x, final double y,
                                  final double w, final double h) {
        if(indicator != null) {
            indicator.layoutChildren();
            indicator.resizeRelocate(0, 0, w, h);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
        return Double.MAX_VALUE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
        return getSkinnable().prefHeight(width);
    }

    /**
     * {@inheritDoc}
     */
//    @Override
//    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
//        return Math.max(100, leftInset + indicator.prefWidth(getSkinnable().getWidth()) + rightInset);
//    }

    @SuppressWarnings("CommentedOutCode")
    private void update() {
        if (indicator != null) {
            indicator.updateSegments(control.getCount(), control.getBits());
        }
    }

    public static class BitsIndicator extends Region {
        private List<Region> segments = new ArrayList<>();

        @SuppressWarnings("unused")
        public BitsIndicator(PiecesIndicator control, PiecesIndicatorSkin s, Paint fillOverride) {
            getChildren().setAll(segments);
            updateSegments(control.getCount(), control.getBits());
        }

        private void updateSegments(int count, BitSet bits) {
            if (segments.size() != count) {
                getChildren().clear();
                segments = IntStream.range(0, count)
                        .mapToObj(i -> {
                            double w = getWidth()/count;
                            Region region = new Region();
                            region.getStyleClass().setAll("segment");
                            region.resizeRelocate(w*i + 0.1*i, 1, w, getHeight());
                            return region;
                        })
                        .collect(Collectors.toList());
                getChildren().setAll(segments);
            }
            if (count > 0 && bits != null) {
                for (int i = bits.nextSetBit(0); i < count && i >=0; i = bits.nextSetBit(i + 1)) {
                    segments.get(i).getStyleClass().setAll("segment", "filled");
                }
            }
            requestLayout();
        }

        private void setFillOverride(Paint fillOverride) {
            //noinspection StatementWithEmptyBody, unused
            if (fillOverride instanceof Color c) {
                //progress.setStyle("-fx-background-color: rgba("+((int)(255*c.getRed()))+","+((int)(255*c.getGreen()))+","+((int)(255*c.getBlue()))+","+c.getOpacity()+");");
            } else {
                //progress.setStyle(null);
            }
        }

        @Override protected void layoutChildren() {
            int i = 0;
            for (Region region : segments) {
                double w = getWidth()/segments.size();
                region.resizeRelocate(w*i + 0.1*i, 1, w, getHeight());
                i++;
            }
        }
    }
}
